using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;

namespace DanielSig.UnityExtensions
{
	public static class XPlane
	{
		public static void RemovePoints<T>(this Plane plane, LinkedList<T> points, Func<T, Vector3> getPoint)
		{
			if(points.Count <= 0) return;
			
			LinkedListNode<T> link = points.First;
			while(link != null)
			{
				LinkedListNode<T> next = link.Next;
				
				Vector3 point = getPoint(link.Value);
				
				if(plane.GetSide(point))
				{
					points.Remove(link);
				}
				
				link = next;
			}
		}
		public static Plane TransformBy(this Plane plane, Matrix4x4 matrix)
		{
			Vector3 normal = matrix.MultiplyVector(plane.normal);
			float d = normal.Dot(matrix.MultiplyPoint3x4(plane.normal * plane.distance));
			return new Plane(normal, d);
		}
		public static Plane From(this Plane plane, Transform localSpace)
		{
			Vector3 globalNormal = plane.normal.FromLocalDirection(localSpace);
			float d = globalNormal.Dot(localSpace.position) + plane.distance;
			return new Plane(globalNormal, d);
		}
		public static Plane To(this Plane plane, Transform localSpace)
		{
			Vector3 localNormal = plane.normal.ToLocalDirection(localSpace);
			float d = localNormal.Dot((plane.normal * plane.distance).To(localSpace));
			return new Plane(localNormal, d);
		}
		public static Vector3 CutLine(this Plane plane, Vector3 p1, Vector3 p2)
		{
			float toP1 = plane.GetDistanceToPoint(p1).Abs();
			float toP2 = plane.GetDistanceToPoint(p2).Abs();
			return p1 + (p2 - p1) * (toP1 / (toP1 + toP2));
		}
		public static Vector3 GetCoordinates(this Plane plane, float x, float y)
		{
			Vector3 right = plane.normal.Cross(Vector3.up);
			Vector3 up = right.Cross(plane.normal);
			
			return plane.normal * plane.distance + right * x + up * y;
		}
		public static Vector3 GetCoordinates(this Plane plane, Vector2 coordinates)
		{
			Vector3 right = plane.normal.Cross(Vector3.up);
			Vector3 up = right.Cross(plane.normal);
			
			return plane.normal * plane.distance + right * coordinates.x + up * coordinates.y;
		}
		public static Vector3 GetUp(this Plane plane)
		{
			return Vector3.up.Flatten(plane).normalized;
		}
		public static Vector3 GetRight(this Plane plane)
		{
			return plane.normal.Cross(Vector3.up);
		}
		public static Vector3 GetDown(this Plane plane)
		{
			return -Vector3.up.Flatten(plane).normalized;
		}
		public static Vector3 GetLeft(this Plane plane)
		{
			return -plane.normal.Cross(Vector3.up);
		}
		public static Plane Mirror(this Plane plane)
		{
			return new Plane(-plane.normal, -plane.distance);
		}
		public static Vector4 ToVector4(this Plane plane)
		{
			return plane.normal.ToVector4(plane.distance);
		}
		public static String ToStringSharp(this Plane plane)
		{
			return plane.ToVector4().ToString();
		}
		public static bool Symmetrical(this Plane plane, Plane other, float threshold)
		{
			return (plane.normal.Aproximates(other.normal, threshold) && plane.distance.Aproximates(other.distance, threshold)) || (plane.normal.Aproximates(-other.normal, threshold) && plane.distance.Aproximates(-other.distance, threshold));
		}
		public static bool Symmetrical(this Plane plane, Plane other)
		{
			return (plane.normal == other.normal && plane.distance == other.distance) || (plane.normal == -other.normal && plane.distance == -other.distance);
		}
		public static Plane[] RemoveSymmetrical(this Plane[] planes)
		{
			return RemoveSymmetrical(planes, 0F);
		}
		public static Plane[] RemoveSymmetrical(this Plane[] planes, float threshold)
		{
			Plane[] newPlanes = new Plane[planes.Length];
			int c = 0;
			for(int i = 0; i < planes.Length; i++)
			{
				Plane current = planes[i];
				bool addcurrent = true;
				for(int j = i; j < planes.Length; j++)
				{
					if(current.Symmetrical(planes[j], threshold))
					{
						addcurrent = false;
						break;
					}
				}
				if(addcurrent)
				{
					newPlanes[c++] = current;
				}
			
			}
			Array.Resize<Plane>(ref newPlanes, c);
			return newPlanes;
		}
	}
}

